package com.common.rediscaffeine;

import com.common.config.CaffeineEntity;
import com.common.config.RedisCaffeineProperties;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @description: Cache 管理器
 * @author：nihongyu
 * @date: 2023/7/18
 */
public class RedisCaffeineCacheManager implements CacheManager {

    private final Logger logger = LoggerFactory.getLogger(RedisCaffeineCacheManager.class);

    private static Map<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>();

    private RedisCaffeineProperties redisCaffeineProperties;

    private RedisTemplate<Object, Object> redisTemplate;


    /**
     * RedisCaffeienCacheConfig 是一个 Bean ,项目启动的时候会加载，里面的 cacheManager() 方法也是一个 Bean 也会加载
     */
    public RedisCaffeineCacheManager(RedisTemplate<Object, Object> stringKeyRedisTemplate,
                                     RedisCaffeineProperties redisCaffeineProperties) {
        this.redisCaffeineProperties = redisCaffeineProperties;
        this.redisTemplate = stringKeyRedisTemplate;
    }


    /**
     * 当使用注解，比如 @Cacheable 时,会先调用此函数 （这是必然的）
     * 只有获取了Cache管理器，才能具体操作缓存
     *
     * @param name
     * @return
     */
    @Override
    public Cache getCache(String name) {
        Cache cache = cacheMap.get(name);
        if (Objects.nonNull(cache)) {
            return cache;
        }
        try {
            cache = new RedisCaffeineCache(name, redisTemplate, createCaffeineCache(name), redisCaffeineProperties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        Cache oldCache = cacheMap.putIfAbsent(name, cache);
        return oldCache == null ? cache : oldCache;
    }

    /**
     * 创建一个 Cache
     */
    public com.github.benmanes.caffeine.cache.Cache createCaffeineCache(String cacheName) throws Exception {

        if (StringUtils.isEmpty(cacheName)) {
            throw new Exception("cacheName 为空");
        }

        Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder();

        List<CaffeineEntity> caffeineEntityList = redisCaffeineProperties.getCaffeineEntityList();
        CaffeineEntity caffeineEntity = null;
        for (int i = 0; i < caffeineEntityList.size(); i++) {
            if (cacheName.equals(caffeineEntityList.get(i).getCacheName())) {
                caffeineEntity = caffeineEntityList.get(i);
                break;
            }
        }
        if (caffeineEntity == null) {
            throw new Exception("cacheName 与配置文件不一致");
        }

        caffeineBuilder.initialCapacity(caffeineEntity.getInitSize());
        caffeineBuilder.maximumSize(caffeineEntity.getMaxSize());
        caffeineBuilder.expireAfterAccess(caffeineEntity.getExpireAfterAccess(), TimeUnit.MINUTES);
        caffeineBuilder.expireAfterWrite(caffeineEntity.getExpireAfterWrite(), TimeUnit.MINUTES);
        caffeineBuilder.recordStats();
        return caffeineBuilder.build();
    }


    @Override
    public Collection<String> getCacheNames() {
        return null;
    }


    public void clearLocal(String cacheName, Object key) {
        //cacheName为null 清除所有进程缓存
        if (cacheName == null) {
            logger.info("清除所有本地缓存");
            cacheMap = new ConcurrentHashMap<>();
            return;
        }

        Cache cache = cacheMap.get(cacheName);
        if (cache == null) {
            return;
        }

        RedisCaffeineCache redisCaffeineCache = (RedisCaffeineCache) cache;
        redisCaffeineCache.clearLocal(key);
    }
}
